import ctypes
import pandas as pd
import math
from util import *
from Task import Task
from Request import Request
from queue import *
import threading
from concurrent.futures import ThreadPoolExecutor


class RequestGenerator:
    
    def init_for_sim(self,cloud_system, args):
        self.sim_flage=True
        #记录到来的用户请求的数量
        self.request_come_num=0
        self.next_interval=0
        self.this_rows_order=0
        
        self.cloud_system=cloud_system
        self.print_level=cloud_system.print_level
        #加载trace数据
        self.file_pd=pd.read_csv(args.validate_trace_file, dtype={'rpc_id': 'str'})
        
        self.all_rows=self.file_pd.shape[0]
        
        self.end_generate=False
        self.request_queue=PriorityQueue()
        self.request_generate_task_queue=Queue()
        return
    
    
    def init_for_analyze(self,file_path, print_level=5):
        self.sim_flage=False
        self.print_level=print_level
        #记录到来的用户请求的数量
        self.request_come_num=0
        self.this_rows_order=0
        self.next_interval=0
        
        #加载trace数据
        self.file_pd=pd.read_csv(file_path, dtype={'rpc_id': 'str'})
        self.all_rows=self.file_pd.shape[0]
        self.list_queue_lock=threading.Lock()
        self.request_queue=PriorityQueue()
        self.request_generate_task_queue=Queue()
        self.end_generate=False
        #获取duration信息
        self.init_duration_info()
    def jump_request(self, jump_num):
        for _ in range(jump_num):
            this_trace_id=self.file_pd.iloc[self.this_rows_order]["traceid"]
            end_rows_order=self.this_rows_order
            #找到连续的traceid对应的数据
            while True:
                if end_rows_order+1<=self.all_rows-1 and this_trace_id==self.file_pd.iloc[end_rows_order+1]["traceid"]:
                    end_rows_order+=1
                else:
                    break
            self.this_rows_order=end_rows_order+1
            if self.this_rows_order>=len(self.file_pd):
                self.this_rows_order=0
        
        
        
        
        
    def generate_request(self, service_name_aim_list=None, size=0):
        if self.print_level>11:
            print(f"Start generate request...")
        this_trace_id=self.file_pd.iloc[self.this_rows_order]["traceid"]
        end_rows_order=self.this_rows_order
        #找到连续的traceid对应的数据
        while True:
            if end_rows_order+1<=self.all_rows-1 and this_trace_id==self.file_pd.iloc[end_rows_order+1]["traceid"]:
                end_rows_order+=1
            else:
                break
        trace_data=self.file_pd.iloc[self.this_rows_order:end_rows_order+1]
        #判断提前退出
        if size>0 and len(trace_data)<size:
            #更新索引    (只有在仿真过程中，才重置索引order)
            self.this_rows_order=end_rows_order+1
            # print(f"索引：{self.this_rows_order}, all:{self.all_rows}")
            # if self.sim_flage==True and self.this_rows_order>=len(self.file_pd):
            #     print(f"request generator end trace (reset row order)!!!")
            #     self.this_rows_order=0
            return None
        service_name_this=trace_data.iloc[0]["service"]
        #########################################中间这一段是生成请求
        request_new=None
        if self.sim_flage==True:
            if self.judge_service_ms_kind_in_history(service_name_f=service_name_this):
                request_new=Request()
                request_id=f"R_{self.request_come_num}"
                request_new.init_for_sim(self.cloud_system, self.cloud_system.env, request_id, trace_data, self.cloud_system.print_level)
                self.judge_service_ms_kind_in_history(request_f=request_new)
                request_new.dynamic_percent=request_new.all_task_num/self.cloud_system.service_to_callgraph_dict[request_new.service_name].all_task_num
            
            #更新下一个请求到来间隔
            if end_rows_order+1<=self.all_rows-1:
                self.next_interval=self.file_pd.iloc[end_rows_order+1]["timestamp"]-self.file_pd.iloc[self.this_rows_order]["timestamp"]
                if self.next_interval<0:
                    print(self.file_pd.iloc[self.this_rows_order-20:end_rows_order+10])
                    print(f"generate_request next interval wrong")
                    exit(-1)
            else:
                # pass
                print(f"request generator end trace (reset next interval)!!!")
                self.next_interval=0    
                
        else:
            if service_name_aim_list==None or service_name_this in service_name_aim_list:
                request_new=Request()
                # print(f"data size{len(trace_data)}")
                request_id=f"R_{self.request_come_num}"
                duration=self.duration_info[trace_data.iloc[0]["service"]]
                request_new.init_for_analyze(request_id, trace_data, duration)
                # a=1
        #########################################生成完毕
        
        #更新索引    (只有在仿真过程中，才重置索引order)
        self.this_rows_order=end_rows_order+1
        # print(f"索引：{self.this_rows_order}, all:{self.all_rows}")
        # if self.sim_flage==True and self.this_rows_order>=len(self.file_pd):
        #     print(f"request generator end trace (reset row order)!!!")
        #     self.this_rows_order=0
        #更新到来的请求数量
        if request_new!=None and request_new.generate_flage==True:
            self.request_come_num+=1
            if self.sim_flage and self.print_level>11:
                print(f"this request trace data:\n{trace_data}")
        if self.print_level>11:
            print(f"end generate request.")
        return request_new if (request_new!=None and request_new.generate_flage==True) else None
    
    
    #这个函数有两种使用方式，一种通过service判断，一种通过生成的request判断（目前功能二选一）
    def judge_service_ms_kind_in_history(self, service_name_f=None, request_f=None):
        if request_f==None:
            if service_name_f not in self.cloud_system.service_to_callgraph_dict.keys():
                return False
            else:
                return True
        else:

            if request_f.service_name not in self.cloud_system.service_to_callgraph_dict.keys():
                request_f.generate_flage=False
                return
            # if not set(request.get_ms_kind_list()).issubset(set(self.cloud_system.ms_kind_list)):
            #     request.generate_flage=False
            if not set(request_f.task_id_list).issubset(set(self.cloud_system.service_to_callgraph_dict[request_f.service_name].task_id_list)):
                request_f.generate_flage=False
                return
    
    # def get_early_end_time(self, request_new, deadline):
    #     early_end_time=float("inf")
    #     for task in request_new.start_task_list:
    #         time_t=task.get_early_end_time(0)
    #         early_end_time=min(early_end_time, time_t)
    #     request_new.early_end_time=early_end_time
    #     if early_end_time> deadline:
    #         request_new.generate_flage=False
        
            
            
    def start(self, thread_num):
        self.thread_num=thread_num
        start_thread = threading.Thread(target=self._start_do)
        start_thread.start()
        
    def _start_do(self):
        producer_thread = threading.Thread(target=self.producer)
        producer_thread.start()
        
        with ThreadPoolExecutor(max_workers=self.thread_num) as executor:
            for _ in range(self.thread_num):
                executor.submit(self.consumer)
        
        producer_thread.join()
        self.end_generate=True
    
    def producer(self):
        request_index=0
        self.this_rows_order=0
        while True:
            this_trace_id=self.file_pd.iloc[self.this_rows_order]["traceid"]
            #初始赋值
            end_rows_order=self.this_rows_order
            #找到连续的traceid对应的数据
            while True:
                if end_rows_order+1<=self.all_rows-1 and this_trace_id==self.file_pd.iloc[end_rows_order+1]["traceid"]:
                    end_rows_order+=1
                else:
                    break
            trace_data=self.file_pd.iloc[self.this_rows_order:end_rows_order+1]
            #更新下一个请求到来间隔
            if end_rows_order+1<=self.all_rows-1:
                next_interval=self.file_pd.iloc[end_rows_order+1]["timestamp"]-self.file_pd.iloc[self.this_rows_order]["timestamp"]
                
            else:
                next_interval=-1
            request_id=f"R_{request_index}"
            trace_line_num=end_rows_order-self.this_rows_order+1
            generate_task=[request_index, request_id, trace_data, next_interval, trace_line_num]
            self.request_generate_task_queue.put(generate_task)
            #更新索引等信息
            request_index+=1
            self.this_rows_order=end_rows_order+1
            if self.is_end(request_index):
                for _ in range(self.thread_num):
                    self.request_generate_task_queue.put(None)
                break
        
    def consumer(self):
        while True:
            task = self.request_generate_task_queue.get()
            if task==None:
                break
            self.generate_request_single(task)
        return
        
    def generate_request_single(self, task):
        [request_index, request_id, trace_data, next_interval, trace_line_num]=task
        request_new=Request()
        if self.sim_flage==True:
            request_new.init_for_sim(self.cloud_system, self.cloud_system.env, request_id, trace_data, self.cloud_system.print_level)
            self.judge_service_ms_kind_in_history(request_new)
        else:
            duration=self.duration_info[trace_data.iloc[0]["service"]]
            request_new.init_for_analyze(request_id, trace_data, duration)
        
        if request_new.generate_flage==False:
            request_new=None
        
        self.request_queue.put((request_index, request_new, next_interval,trace_line_num))
        return
        
    
    
        
        
            
            
    def init_duration_info(self):
        self.duration_info={}
        for index,row in self.file_pd.iterrows():
            if row["um"]=="USER" and row["rpctype"]=="http" and str(row["rpc_id"])=='0':
                service_name=row["service"]
                rt=row["rt"]
                if service_name not in self.duration_info.keys():
                    self.duration_info[service_name]=rt
                else:
                    self.duration_info[service_name]=max(self.duration_info[service_name],rt)
        return 
    
    def get_next_interval(self):
        return self.next_interval
    
    #完事儿后循环 
    def is_end(self, generate_task_num=-1):
        if generate_task_num==-1:
            generate_task_num=self.request_come_num
        if self.sim_flage:
            if self.cloud_system.args.request_num<0:
                return self.this_rows_order>self.all_rows-1
            if generate_task_num>=self.cloud_system.args.request_num+self.cloud_system.args.pre_request_num or self.this_rows_order>self.all_rows-1:
                return True
            else:
                return False
        else:
            return self.this_rows_order>self.all_rows-1
    def generate_failure(self):
        self.request_come_num-=1
        
    def is_end_for_analyze(self):
        return self.this_rows_order>self.all_rows-1
    
    
    

    
    
    
    
   
    
    
    
 